﻿using System;
using System.Collections.Generic;
using WDFactoryLib;

namespace CardsEngine.Poker
{
    public class Hand: FactoryUser, IList<Card>
    {
        // Both of these values should be prime numbers
        // (used for generating hash codes)
        private const int HASH_START_VALUE = 17;
        private const int HASH_MULTIPLIER = 23;

        private IList<Card> cards;

        public Hand()
            :base()
        {
            InitializeInstance();
        }

        internal Hand(IFactory _factory)
            :base(_factory)
        {
            InitializeInstance();
        }

        public Hand(IEnumerable<Card> cards_src)
            :base()
        {
            InitializeInstance(cards_src);
        }

        internal Hand(IFactory _factory, IEnumerable<Card> cards_src)
            :base(_factory)
        {
            InitializeInstance(cards_src);
        }

        private void InitializeInstance(IEnumerable<Card> cards_src = null)
        {
            if (cards_src == null)
                cards = Factory.GetNewList<Card>();
            else
                cards = Factory.GetNewList<Card>(cards_src);
        }

        public override int GetHashCode()
        {
            int result = HASH_START_VALUE;

            unchecked
            {
                // Use a for instead of foreach to ensure that we iterate
                // in ascending order by index
                for (int x = 0; x < Count; ++x)
                    result = result * HASH_MULTIPLIER + cards[x].GetHashCode();
            }

            return result;
        }

        /// <summary>
        /// Two Hands are equal if they have the same cards at the same indices
        /// </summary>
        /// <param name="obj">The object to compare</param>
        /// <returns>true if the object is equal; false otherwise</returns>
        public override bool Equals(object obj)
        {
            Hand other = obj as Hand;

            if (other == null || other.Count != this.Count)
                return false;

            for (int x = 0; x < this.Count; ++x)
            {
                if (!this[x].Equals(other[x]))
                    return false;
            }

            return true;
        }

        #region IList<Card> Members

        public int IndexOf(Card item)
        {
            return cards.IndexOf(item);
        }

        public void Insert(int index, Card item)
        {
            cards.Insert(index, item);
        }

        public void RemoveAt(int index)
        {
            cards.RemoveAt(index);
        }

        public Card this[int index]
        {
            get
            {
                return cards[index];
            }
            set
            {
                cards[index] = value;
            }
        }

        public void Add(Card item)
        {
            cards.Add(item);
        }

        public void Clear()
        {
            cards.Clear();
        }

        public bool Contains(Card item)
        {
            return cards.Contains(item);
        }

        public void CopyTo(Card[] array, int arrayIndex)
        {
            cards.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return cards.Count; }
        }

        public bool IsReadOnly
        {
            get { return cards.IsReadOnly; }
        }

        public bool Remove(Card item)
        {
            return cards.Remove(item);
        }

        public IEnumerator<Card> GetEnumerator()
        {
            return cards.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return cards.GetEnumerator();
        }

        #endregion
    }
}
